package net.mayee.alice.web.controller.admin.system;

import net.mayee.alice.entity.admin.account.Role;
import net.mayee.alice.entity.admin.account.User;
import net.mayee.alice.entity.menu.sidebar.SidebarMenu;
import net.mayee.alice.entity.permission.Action;
import net.mayee.alice.entity.permission.Permission;
import net.mayee.alice.filter.ShiroAdminDbRealm;
import net.mayee.alice.web.controller.base.BaseController;
import net.mayee.alice.web.utils.UUIDGenerator;
import net.mayee.common.MenuHelper;
import net.mayee.common.PermissionHelper;
import net.mayee.datatables.DtBean;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.json.JSONArray;
import org.json.JSONException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

@Controller
public class RoleController extends BaseController {

    private static final Pattern ROLE_KEY_PATTERN = Pattern
            .compile("^([a-zA-Z0-9]{4,20})$");

    @RequiresPermissions(value = {"role:R"})
    @RequestMapping(value = "/sdm/sys/role/R")
    public String roleR(Model model) {
        return "system/role/roleR";
    }

    @RequiresPermissions(value = {"role:R"})
    @RequestMapping(value = "/sys/role/ajax/R")
    public void roleRAjax(HttpServletResponse response, HttpServletRequest request) throws IOException {

        JSONArray array = new JSONArray();
        DtBean dtBean = new DtBean(request);

        int totalRecords = this.getRoleService().count(new HashMap<String, Object>());
        int totalDisplayRecords = totalRecords;
        if (dtBean.isHasSearch()) {
            totalDisplayRecords = this.getRoleService().count(dtBean.getSearchParams());
        }

        /* 权限判断 */
        Subject subject = SecurityUtils.getSubject();
        String USER_ROLE_U_disabledStr = "";
        String ROLE_PERMISSION_U_disabledStr = "";
        if (!subject.isPermitted("role:USER_ROLE_U")) {
            USER_ROLE_U_disabledStr = "disabled=\"disabled\"";
        }
        if (!subject.isPermitted("role:ROLE_PERMISSION_U")) {
            ROLE_PERMISSION_U_disabledStr = "disabled=\"disabled\"";
        }

        List<Role> list = this.getRoleService().searchByLimit(dtBean.getSearchParams());
        for (int i = 0; i < list.size(); i++) {
            Role role = list.get(i);
            JSONArray ja = new JSONArray();
            ja.put("<a href=\"" + getPath() + "/sys/role/UR/" + role.getUuid() + "\" " + USER_ROLE_U_disabledStr + " class=\"btn default btn-xss blue\"> <i class=\"fa fa-group\" style=\"font-size:12px;margin:0px\"></i></a>");
            ja.put("<a href=\"" + getPath() + "/sys/role/RP/" + role.getUuid() + "\" " + ROLE_PERMISSION_U_disabledStr + " class=\"btn default btn-xss green\"> <i class=\"fa fa-key\" style=\"font-size:12px\"></i></a>");
            ja.put("<a style=\"text-decoration: underline;\" href=\"" + getPath() + "/sys/role/" + role.getUuid() + "\">" + role.getRoleKey() + "</a>");
            ja.put(role.getName());
            ja.put(role.getNote());
            array.put(ja);
        }

        request.setAttribute("reJSON", dtBean.getJSONString(array, totalRecords, totalDisplayRecords));
    }

    @RequiresPermissions(value = {"role:USER_ROLE_U"})
    @RequestMapping(value = "/sys/role/UR/{u}")
    public String userRoleU(@PathVariable(value = "u") String uid, Model model) {
        Role role = this.getRoleService().getRole(uid);
        if (role == null) {
            return "redirect:/sdm/sys/role/R";
        }
        /* get choose user */
        List<User> chooseUserList = this.getUserService().getUsersByRole(uid);

        /* get all user and remove choose user */
        List<User> noAdminUserList = this.getUserService().getAllNoAdmin();
        noAdminUserList.removeAll(chooseUserList);

        model.addAttribute("role", role);
        model.addAttribute("noAdminUserList", noAdminUserList);
        model.addAttribute("chooseUserList", chooseUserList);
        return "system/role/userRoleU";
    }

    @RequiresPermissions(value = {"role:USER_ROLE_U"})
    @RequestMapping(value = "/sys/role/UR/ajax/{u}", method = RequestMethod.POST)
    public void userRoleUAjax(@PathVariable(value = "u") String uid, HttpServletResponse response,
                              HttpServletRequest request, Model model) throws IOException, JSONException {
        List<String> errorMsgList = new ArrayList<String>();

        /* 已选用户的JSON字符串 */
        String j = request.getParameter("j");
        Role role = this.getRoleService().getRole(uid);
        if (StringUtils.isBlank(j)
                || StringUtils.isBlank(uid) || role == null) {
            errorMsgList.add(getMessage("net.mayee.alice.error.data_error", "数据异常！"));
        } else {
            String backVal = this.getRoleService().saveUserRole(role, j);
            if (backVal != null) {
                errorMsgList.add(this.getMessage("net.mayee.alice.msg.sys.role.save_roleuser_error",
                        new String[]{backVal},
                        "用户[ " + backVal + " ]不可以拥有超过三个以上角色!"));
            }
        }

        request.setAttribute("reJSON", this.getAjaxUpdateJSONString(errorMsgList));
    }

    @RequiresPermissions(value = {"role:ROLE_PERMISSION_U"})
    @RequestMapping(value = "/sys/role/RP/{u}")
    public String rolePermissionU(@PathVariable(value = "u") String uid, Model model) {
        Role role = this.getRoleService().getRole(uid);
        if (role == null) {
            return "redirect:/sdm/sys/role/R";
        }

        /* get all permission */
        Map<String, Permission> permissionsMap = PermissionHelper.getInstance().getPermissionsMap();
        /* get all menu */
        List<SidebarMenu> sbmList = MenuHelper.getInstance().getSidebarMenus().getSidebarMenu();
        /* choose permission */
        HashMap<String, String> permissionsChooseMap = this.getRoleService().getPermissionsByRole(uid);

        model.addAttribute("role", role);
        model.addAttribute("permissionsTableString",
                printPermissionTableString(sbmList, permissionsMap, permissionsChooseMap));
        return "system/role/rolePermissionU";
    }

    @RequiresPermissions(value = {"role:ROLE_PERMISSION_U"})
    @RequestMapping(value = "/sys/role/RP/ajax/{u}", method = RequestMethod.POST)
    public void rolePermissionUAjax(@PathVariable(value = "u") String uid, HttpServletResponse response,
                                    HttpServletRequest request, Model model) throws IOException, JSONException {
        List<String> errorMsgList = new ArrayList<String>();

        Role role = this.getRoleService().getRole(uid);
        if (role != null) {
            /* 更新角色的权限 */
            role.setPermissions(this.buildSavePermissionString(request));
            this.getRoleService().updateRole(role);
            /* 刷新SHIRO中角色的权限 */
            ShiroAdminDbRealm.clearSomeCachedAuthorizationInfo(request,
                    this.getUserService().getNamesByRole(uid));
        } else {
            errorMsgList.add(
                    this.getMessage("net.mayee.alice.msg.sys.role.role_does_not_exist", "角色不存在！"));
        }

        request.setAttribute("reJSON",
                this.getAjaxUpdateJSONString(errorMsgList,
                        this.getMessage("net.mayee.alice.msg.sys.role.permission_update_success", "权限更新成功！")));
    }

    @RequiresPermissions(value = {"role:C"})
    @RequestMapping(value = "/sys/role/C")
    public String roleC(Model model) {
        return "system/role/roleC";
    }

    @RequiresPermissions(value = {"role:C"})
    @RequestMapping(value = "/sys/role/ajax/C", method = RequestMethod.POST)
    public void roleCAjax(@ModelAttribute("role") Role role, HttpServletResponse response,
                          HttpServletRequest request, Model model) throws IOException, JSONException {
        List<String> errorMsgList = validator(role);

        if (this.getRoleService().getByRoleKey(role.getRoleKey().trim()) == null) {
            role.setUuid(UUIDGenerator.getUUID());
            this.getRoleService().insertRole(role);
        } else {
            errorMsgList.add(this.getMessage("net.mayee.alice.msg.sys.role.role_key_exist",
                    new String[]{role.getRoleKey()}, "[ " + role.getRoleKey() + " ] 已存在；"));
        }

        String successMsg = this.getMessage("net.mayee.alice.msg.sys.role.create_role_success",
                new String[]{role.getRoleKey()}, "新增用户 [ " + role.getRoleKey() + " ] 成功！");
        request.setAttribute("reJSON",
                this.getAjaxRegisterJSONString(errorMsgList, successMsg));
    }

    @RequiresPermissions(value = {"role:U"})
    @RequestMapping(value = "/sys/role/ajax/U", method = RequestMethod.POST)
    public void roleUAjax(@ModelAttribute("role") Role role, HttpServletResponse response,
                          HttpServletRequest request, Model model) throws IOException, JSONException {
        List<String> errorMsgList = validator(role);

        /* 先验证uuid是否合法 */
        Role cRole = this.getRoleService().getRole(role.getUuid());
        if (cRole != null) {

            String newRoleKey = role.getRoleKey().trim();
            /* 对比roleKey是否修改 */
            if (!cRole.getRoleKey().equals(newRoleKey)) {
                Role someRole = this.getRoleService().getByRoleKey(newRoleKey);
                if (someRole == null || someRole.getUuid().equals(role.getUuid())) {
                    this.getRoleService().updateRole(role);
                } else {
                    errorMsgList.add(this.getMessage("net.mayee.alice.msg.sys.role.role_key_exist",
                            new String[]{role.getRoleKey()}, "[ " + role.getRoleKey() + " ] 已存在；"));
                }
            }
        } else {
            errorMsgList.add(
                    this.getMessage("net.mayee.alice.msg.sys.role.role_does_not_exist", "角色不存在！"));
        }

        request.setAttribute("reJSON",
                this.getAjaxUpdateJSONString(errorMsgList,
                        this.getMessage("net.mayee.alice.global.save_success", "保存成功！")));
    }

    @RequiresPermissions(value = {"role:R"})
    @RequestMapping(value = "/sys/role/{u}")
    public String roleU(@PathVariable(value = "u") String uid, Model model) {
        Role role = this.getRoleService().getRole(uid);
        if (role == null) {
            return "redirect:/sdm/sys/role/R";
        }
        model.addAttribute("role", role);
        return "system/role/roleU";
    }

    @RequiresPermissions(value = {"role:D"})
    @RequestMapping(value = "/sys/role/ajax/D/{u}", method = RequestMethod.POST)
    public void roleDAjax(@PathVariable(value = "u") String uid, HttpServletResponse response,
                          HttpServletRequest request, Model model) throws IOException, JSONException {
        List<String> errorMsgList = new ArrayList<String>();
        Role role = this.getRoleService().getRole(uid);
        if (role != null) {
            /* 验证角色下是否有用户 */
            List<User> userList = this.getUserService().getUsersByRole(role.getUuid());
            if (userList.size() < 1) {
                this.getRoleService().removeRole(role.getUuid());
            } else {
                errorMsgList.add(
                        this.getMessage("net.mayee.alice.msg.sys.role.cannot_delete_exist_user", "无法删除角色，请先移除该角色下所有用户！"));
            }
        } else {
            errorMsgList.add(
                    this.getMessage("net.mayee.alice.msg.sys.role.role_does_not_exist", "角色不存在！"));
        }
        request.setAttribute("reJSON",
                this.getAjaxUpdateJSONString(errorMsgList,
                        this.getMessage("net.mayee.alice.global.delete_success", "删除成功！")));
    }

    private List<String> validator(Role role) {
        List<String> errorMsgList = new ArrayList<String>();

        if (!ROLE_KEY_PATTERN.matcher(role.getRoleKey().trim()).matches()) {
            errorMsgList.add(this.getMessage("net.mayee.alice.msg.sys.role.role_key_error",
                    new String[]{this.getMessage("net.mayee.alice.web.sys.role.role_key")},
                    "[ 标识 ] 只能包含大小写字母和数字，长度4 ~ 20个字符；"));
        }

        String name = role.getName().trim();
        if (name.length() < 2 || name.length() > 20) {
            errorMsgList.add(this.getMessage("net.mayee.alice.msg.sys.role.name_error",
                    new String[]{this.getMessage("net.mayee.alice.web.sys.role.name")},
                    "[ 名称 ] 长度在2 ~ 20个字符之间；"));
        }

        return errorMsgList;
    }

    private String buildSavePermissionString(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        Map<String, String> map = new HashMap<>();

        Enumeration paramNames = request.getParameterNames();
        Map<String, Object> params = new TreeMap<String, Object>();
        while (paramNames != null && paramNames.hasMoreElements()) {
            String paramName = (String) paramNames.nextElement();
            if (paramName.startsWith("PER-")) {
                String[] values = request.getParameterValues(paramName);
                if (values != null && values.length == 1 && "1".equals(values[0])) {
                    String menuId = paramName.split("-")[1];
                    String key = paramName.split("-")[2];
                    if (map.containsKey(menuId)) {
                        String p = map.get(menuId);
                        map.put(menuId, p + "|" + key);
                    } else {
                        map.put(menuId, key);
                    }
                }
            }
        }
        Set<String> keySet = map.keySet();
        for (String menuId : keySet) {
            sb.append(menuId).append(":").append(map.get(menuId)).append(",");
        }
        return sb.substring(0, sb.length() - 1);
    }


    private String printPermissionTableString(List<SidebarMenu> sbmList,
                                              Map<String, Permission> permissionsMap,
                                              HashMap<String, String> permissionsChooseMap) {
        StringBuilder sb = new StringBuilder();
        sb.append("<table id=\"permissionsTable\" class=\"table table-bordered table-striped table-condensed flip-content\">")
                .append("<thead class=\"flip-content\">")
                .append("<tr>")
                .append("<th width=\"30%\" style=\"text-align: center\">")
                .append(this.getMessage("net.mayee.alice.web.sys.role.menu"))
                .append("</th>")
                .append("<th width=\"*\" style=\"text-align: center\">")
                .append(this.getMessage("net.mayee.alice.web.sys.role.permission"))
                .append("</th></tr></thead>")
                .append("<tbody>");

        if (sbmList.size() > 0) {
            for (SidebarMenu sbm : sbmList) {

                /* 先循环子菜单，统计是否全被选中 */
                StringBuilder s_sb = new StringBuilder(); //临时保存子菜单的字符串
                boolean isAllSubMChecked = false; //全选中标识
                if (sbm.isHadSubMenu()) {
                    isAllSubMChecked = true;
                    List<SidebarMenu> subML = sbm.getSubMenuList();
                    for (SidebarMenu subM : subML) {
                        Permission per2 = permissionsMap.get(subM.getId());
                        if (per2 == null) {
                            continue;
                        }

                        /* 先循环子菜单的权限，统计是否全被选中 */
                        StringBuilder a_sb = new StringBuilder(); //临时保存子菜单权限的字符串
                        boolean isAllActionChecked = true; //全选中标识
                        List<Action> actionList = per2.getActionList();
                        for (Action action : actionList) {
                            String kList = permissionsChooseMap.get(action.getMenuId());
                            String checkedStr = printPermissionString(action.getKey(), kList);
                            if (StringUtils.isBlank(checkedStr)) {
                                isAllActionChecked = false;
                            }
                            a_sb.append("<label class=\"ck_label\">")
                                    .append("<input type=\"checkbox\" class=\"")
                                    .append(sbm.getId())
                                    .append("-ck\" value=\"1\" ")
                                    .append(checkedStr)
                                    .append(" name=\"PER-")
                                    .append(subM.getId())
                                    .append("-")
                                    .append(action.getKey())
                                    .append("\"/>")
                                    .append(action.getText())
                                    .append("&nbsp;&nbsp;")
                                    .append("</label>");
                        }

                        //生成子菜单字符串，最后会加上子菜单权限字符串a_sb
                        s_sb.append("<tr><td style=\"padding-left: 22px;\" class=\"ckgTd\">")
                                .append("<label class=\"ck_label\">")
                                .append("<input type=\"checkbox\" ");
                        if (isAllActionChecked) {
                            s_sb.append("checked");
                        } else {
                            isAllSubMChecked = false;
                        }
                        s_sb.append(" class=\"")
                                .append(sbm.getId())
                                .append("-ck\" data-set=\"#td-")
                                .append(sbm.getId())
                                .append("-")
                                .append(subM.getId())
                                .append(" .")
                                .append(sbm.getId())
                                .append("-ck\"/>")
                                .append(subM.getName())
                                .append("</label></td>");
                        s_sb.append("<td class=\"ckTd\" id=\"td-")
                                .append(sbm.getId())
                                .append("-")
                                .append(subM.getId())
                                .append("\">");
                        s_sb.append(a_sb);
                        s_sb.append("</td></tr>");
                    }
                }

                //父菜单
                sb.append("<tr style=\"background-color:#e9edef\">")
                        .append("<td class=\"ckgTd\">")
                        .append("<label class=\"ck_label\">");
                //无子菜单，有链接；e.g.HOME
                if (!sbm.isHadSubMenu() && sbm.getHref() != null && !"javascript:;".equals(sbm.getHref())) {
                    sb.append("<input type=\"checkbox\" data-set=\"#td-")
                            .append(sbm.getId())
                            .append(" .at-ck\"/>");
                } //有子菜单，无链接；标准的父菜单
                else if (sbm.isHadSubMenu() && sbm.getHref() == null) {
                    sb.append("<input type=\"checkbox\" ");
                    if (isAllSubMChecked) {
                        sb.append("checked");
                    }
                    sb.append(" data-set=\".")
                            .append(sbm.getId())
                            .append("-ck\"/>");
                } //无子菜单，无链接
                else {
                    sb.append("<input type=\"checkbox\" disabled/>");
                }
                sb.append(sbm.getName()).append("</label></td>");

                Permission per1 = permissionsMap.get(sbm.getId());
                sb.append("<td id=\"td-").append(sbm.getId()).append("\">");
                if (per1 != null) {
                    List<Action> actionList = per1.getActionList();
                    for (Action action : actionList) {
                        sb.append("<label class=\"ck_label\">")
                                .append("<input type=\"checkbox\" class=\"at-ck\" value=\"1\" name=\"PER-")
                                .append(sbm.getId())
                                .append("-")
                                .append(action.getKey())
                                .append("\"/>")
                                .append(action.getText())
                                .append("&nbsp;&nbsp;</label>")
                                .append("</td></tr>");
                    }
                }
                //父菜单 END
                sb.append(s_sb);
            }
        } else {
            sb.append("<tr><td colspan=\"2\">")
                    .append(this.getMessage("net.mayee.alice.web.sys.role.no_permission"))
                    .append("</td></tr>");
        }
        sb.append("</tbody>").append("</table>");
        return sb.toString();
    }

    /**
     * @param p   权限的KEY
     * @param str 角色已有的权限字符串 e.g. C|R|U
     * @return 成功返回"CHECKED"字符串，失败返回""
     */
    private String printPermissionString(String p, String str) {
        if (StringUtils.isNotEmpty(str)) {
            String[] kList = str.split("\\|");
            for (String key : kList) {
                if (key.equalsIgnoreCase(p)) {
                    return "checked";
                }
            }
        }
        return "";
    }

}
